﻿Imports System.Data.OleDb
Imports System.Globalization
Imports System.IO
Imports Microsoft.ML
Imports Microsoft.ML.Data
Imports Microsoft.Office.Interop.Access

Public Class Tera計算EIQAIFrom
＃Region "変数処理"
    ' ========================================
    ' クラスレベル変数
    ' ========================================
    Private mlContext As MLContext
    Private trainedModels As Dictionary(Of String, ITransformer)
    ' 予測エンジンのキャッシュ用
    Private predictionEngines As Dictionary(Of String, PredictionEngine(Of InputData, SinglePrediction))
    Private modelFolder As String
    Private 出力フィールド名リスト As List(Of String)
    ' マトリクス表が有効になっているか（EIQマトリクス表実行Bt が押された後に True）
    Private matrixActive As Boolean = False
    ' T760 テーブルのレコード数 (計算用最大値)
    Private EMax As Integer = 0
    Private IMax As Integer = 0
    ' アイテム数補正の許容差（差がこの値以下なら入力値で上書きする）
    Private Const ITEM_TOLERANCE As Integer = 1

    ' ヘルパー: 文字列を四捨五入して整数文字列にする
    Private Function ToRoundedString(s As String) As String
        If String.IsNullOrWhiteSpace(s) Then
            Return String.Empty
        End If
        Dim d As Double
        If Double.TryParse(s, Globalization.NumberStyles.Any, Globalization.CultureInfo.InvariantCulture, d) Then
            Return CInt(Math.Round(d)).ToString()
        ElseIf Double.TryParse(s, Globalization.NumberStyles.Any, Globalization.CultureInfo.CurrentCulture, d) Then
            Return CInt(Math.Round(d)).ToString()
        Else
            Return String.Empty
        End If
    End Function

    '' T710 テーブル作成: T410 のロジックと同様に乱数に基づいて判定テーブルを作成する
    'Private Sub T710テーブル作成(tableName As String, 採用比率 As Double, seed As Integer)
    '    If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Throw New Exception("DBファイルが見つかりません")

    '    Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
    '        conn.Open()

    '        ' テーブル削除（存在する場合）
    '        Using cmdDrop As New OleDbCommand($"DROP TABLE [{tableName}]", conn)
    '            Try
    '                cmdDrop.ExecuteNonQuery()
    '            Catch
    '                ' 無視
    '            End Try
    '        End Using

    '        ' 採用比率が1以下ならT410テーブル作成1の方式（乱数を生成）
    '        If 採用比率 <= 1 Then
    '            ' シードに基づく乱数生成で指定件数分のレコードを作成する。
    '            ' 件数は T160 の順位最大値を利用。
    '            Dim cnt As Integer
    '            If tableName.Contains("_E") Then
    '                cnt = GetMaxRankFromTable("T160_全出荷E集計")
    '            Else
    '                cnt = GetMaxRankFromTable("T160_全出荷I集計")
    '            End If

    '            Dim rnd As New Random(seed)

    '            ' 目標件数を算出（比率 * 件数）して、整数で確実にその件数になるようにする
    '            Dim desiredCount As Integer = CInt(Math.Round(採用比率 * cnt))
    '            If desiredCount < 0 Then desiredCount = 0
    '            If desiredCount > cnt Then desiredCount = cnt

    '            ' CREATE TABLE
    '            Dim createSql As String
    '            If tableName.Contains("_E") Then
    '                createSql = $"CREATE TABLE [{tableName}] (順位 AUTOINCREMENT PRIMARY KEY, 乱数 DOUBLE, 判定 VARCHAR(10), 出荷先名 VARCHAR(255))"
    '            Else
    '                createSql = $"CREATE TABLE [{tableName}] (順位 AUTOINCREMENT PRIMARY KEY, 乱数 DOUBLE, 判定 VARCHAR(10), アイテム VARCHAR(255))"
    '            End If
    '            Using cmdCreate As New OleDbCommand(createSql, conn)
    '                cmdCreate.ExecuteNonQuery()
    '            End Using

    '            ' INSERT
    '            Dim insertSql As String = $"INSERT INTO [{tableName}] (乱数, 判定) VALUES (?, ?)"
    '            Using cmdIns As New OleDbCommand(insertSql, conn)
    '                cmdIns.Parameters.Add("@乱数", OleDbType.Double)
    '                cmdIns.Parameters.Add("@判定", OleDbType.VarChar, 10)
    '                For i As Integer = 1 To cnt
    '                    Dim rv As Double = rnd.NextDouble()
    '                    ' deterministic: 上位 desiredCount 件を採用 (Yes) とする
    '                    Dim 判定 As String = If(i <= desiredCount, "Yes", "No")
    '                    cmdIns.Parameters("@乱数").Value = rv
    '                    cmdIns.Parameters("@判定").Value = 判定
    '                    cmdIns.ExecuteNonQuery()
    '                Next
    '            End Using

    '            ' T160 から名称を取り込む
    '            Dim updateSql As String
    '            If tableName.Contains("_E") Then
    '                updateSql = $"UPDATE [{tableName}] LEFT JOIN T160_全出荷E集計 ON [{tableName}].順位 = T160_全出荷E集計.順位 SET [{tableName}].出荷先名 = [T160_全出荷E集計]![出荷先];"
    '            Else
    '                updateSql = $"UPDATE [{tableName}] LEFT JOIN T160_全出荷I集計 ON [{tableName}].順位 = T160_全出荷I集計.順位 SET [{tableName}].アイテム = [T160_全出荷I集計]![アイテム];"
    '            End If
    '            Using cmdUpd As New OleDbCommand(updateSql, conn)
    '                cmdUpd.ExecuteNonQuery()
    '            End Using

    '        Else
    '            ' 採用比率 > 1 の場合は T410作成2 の方式（T160 の乱数をそのまま取り込む）
    '            Dim src As String = If(tableName.Contains("_E"), "T160_全出荷E集計", "T160_全出荷I集計")
    '            Dim fld As String = If(tableName.Contains("_E"), "出荷先", "アイテム")
    '            Dim tname As String = If(tableName.Contains("_E"), "出荷先名", "アイテム")

    '            Dim createSelectSql As String = $"SELECT 順位, 乱数, IIF(乱数 <= {採用比率 - 1}, 'Yes', 'No') AS 判定, {fld} AS {tname} INTO [{tableName}] FROM {src}"
    '            Using cmd As New OleDbCommand(createSelectSql, conn)
    '                cmd.ExecuteNonQuery()
    '            End Using
    '        End If
    '    End Using
    'End Sub

    ' T600 の集計結果を UI の実計算Gb のテキストと 実計算Dgv に反映する
    Private Sub Update実計算表示FromT600()
        If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Return

        Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
            conn.Open()

            ' T600_手計算 が存在するか確認
            Dim schema = conn.GetSchema("Tables")
            Dim found As Boolean = False
            For Each row As DataRow In schema.Rows
                If row("TABLE_NAME").ToString().ToUpper() = "T600_手計算".ToUpper() Then
                    found = True
                    Exit For
                End If
            Next
            If Not found Then Return

            ' 単一行を取得
            Dim sql As String = "SELECT * FROM T600_手計算"
            Dim dt As New DataTable()
            Using da As New OleDbDataAdapter(sql, conn)
                da.Fill(dt)
            End Using
            If dt.Rows.Count = 0 Then Return

            Dim r As DataRow = dt.Rows(0)

            ' 基本フィールドをテキストボックスに設定
            Try
                TextBox27.Text = If(dt.Columns.Contains("出荷先数") AndAlso Not IsDBNull(r("出荷先数")), Convert.ToDouble(r("出荷先数")).ToString("F0"), "")
                TextBox27.Text = "出荷先数"
            Catch
            End Try
            Try
                TextBox26.Text = "アイテム数"
                実_IAI_Tb.Text = If(dt.Columns.Contains("アイテム数") AndAlso Not IsDBNull(r("アイテム数")), Convert.ToDouble(r("アイテム数")).ToString("F0"), "")
            Catch
            End Try

            ' pred 辞書を作成（予測側のロジックと同様のキー）
            Dim pred As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
            ' E1..E5, I1..I5
            For i As Integer = 1 To 5
                Dim ec = $"E{i}"
                If dt.Columns.Contains(ec) AndAlso Not IsDBNull(r(ec)) Then pred(ec) = Convert.ToDouble(r(ec)).ToString("F0") Else pred(ec) = "0"
                Dim ic = $"I{i}"
                If dt.Columns.Contains(ic) AndAlso Not IsDBNull(r(ic)) Then pred(ic) = Convert.ToDouble(r(ic)).ToString("F0") Else pred(ic) = "0"
            Next
            ' 行数01-25, バラ数01-25, ケース01-25, PL01-25, 容積01-25, 重量01-25
            Dim prefixes As String() = {"行数", "バラ数", "ケース", "PL", "容積", "重量"}
            For Each pfx As String In prefixes
                For i As Integer = 1 To 25
                    Dim colName = pfx & i.ToString("D2")
                    If dt.Columns.Contains(colName) AndAlso Not IsDBNull(r(colName)) Then
                        pred(colName) = Convert.ToDouble(r(colName)).ToString("F0")
                    Else
                        pred(colName) = "0"
                    End If
                Next
            Next

            ' 実計算Gb のテキストボックスに E1..E5 を設定（左縦）
            Try
                実_EAI_Tb.Text = If(pred.ContainsKey("E1"), pred("E1"), "")
                実_EA2_Tb.Text = If(pred.ContainsKey("E2"), pred("E2"), "")
                実_EB_Tb.Text = If(pred.ContainsKey("E3"), pred("E3"), "")
                実_EC_Tb.Text = If(pred.ContainsKey("E4"), pred("E4"), "")
                実_ED_Tb.Text = If(pred.ContainsKey("E5"), pred("E5"), "")
                ' 合計（左下）
                Dim sumE As Long = 0
                For i As Integer = 1 To 5
                    sumE += ToLongOrZero(If(pred.ContainsKey($"E{i}"), pred($"E{i}"), "0"))
                Next
                実_EKK_Tb.Text = sumE.ToString()
            Catch
            End Try

            ' 実計算Gb のテキストボックスに I1..I5 を設定（上横）
            Try
                実_IAI_Tb.Text = If(pred.ContainsKey("I1"), pred("I1"), "")
                実_IA2_Tb.Text = If(pred.ContainsKey("I2"), pred("I2"), "")
                実_IB_Tb.Text = If(pred.ContainsKey("I3"), pred("I3"), "")
                実_IC_Tb.Text = If(pred.ContainsKey("I4"), pred("I4"), "")
                実_ID_Tb.Text = If(pred.ContainsKey("I5"), pred("I5"), "")
                Dim sumI As Long = 0
                For i As Integer = 1 To 5
                    sumI += ToLongOrZero(If(pred.ContainsKey($"I{i}"), pred($"I{i}"), "0"))
                Next
                実_IKK_Tb.Text = sumI.ToString()
            Catch
            End Try

            ' マトリクス（実計算Dgv）：AI予測EIQマトリクス表_複写Dgv と同じ単一メトリクス(5x5)表示にする
            If 実計算Dgv Is Nothing Then Return
            実計算Dgv.Rows.Clear()
            実計算Dgv.Columns.Clear()

            ' 表示単位を比較タブのラジオボタンから決定（AI予測と同じ振る舞い）
            Dim prefix As String = ""
            If 抽出_出庫回数2Rb.Checked Then prefix = "行数"
            If 抽出_バラ2Rb.Checked Then prefix = "バラ数"
            If 抽出_ケース2Rb.Checked Then prefix = "ケース"
            If 抽出_PL2Rb.Checked Then prefix = "PL"
            If 抽出_容積2Rb.Checked Then prefix = "容積"
            If 抽出_重量2Rb.Checked Then prefix = "重量"

            ' 列ヘッダーを AI 予測複写 と同じにする
            実計算Dgv.Columns.Add("GPLEランク", "GPLEランク")
            実計算Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            実計算Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            実計算Dgv.Columns.Add("GPLI_B", "GPLI_B")
            実計算Dgv.Columns.Add("GPLI_C", "GPLI_C")
            実計算Dgv.Columns.Add("GPLI_D", "GPLI_D")
            実計算Dgv.Columns.Add("横計", "横計")

            ' 列幅調整: 0列は90, others 83
            For i As Integer = 0 To 実計算Dgv.Columns.Count - 1
                If i = 0 Then
                    実計算Dgv.Columns(i).Width = 90
                Else
                    実計算Dgv.Columns(i).Width = 83
                End If
            Next

            ' 行ラベル
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                実計算Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next

            ' 下に合計行を確保
            Const dataRows As Integer = 5
            Dim totalsRowIndex As Integer = dataRows
            While 実計算Dgv.Rows.Count < dataRows + 1
                実計算Dgv.Rows.Add()
            End While

            ' 埋め込み: prefix に基づく 5x5 の値埋め
            Dim lastColIndex As Integer = 実計算Dgv.Columns.Count - 1
            For rr As Integer = 0 To dataRows - 1
                For cc As Integer = 1 To 5
                    Dim idx As Integer = rr * 5 + (cc - 1)
                    Dim k As Integer = idx + 1
                    Dim fieldName As String = prefix & k.ToString("D2")
                    Dim value As String = ""
                    If pred.ContainsKey(fieldName) Then value = pred(fieldName)
                    実計算Dgv.Rows(rr).Cells(cc).Value = value
                Next
            Next

            ' 横計
            For rr As Integer = 0 To dataRows - 1
                Dim rowSum As Long = 0
                For cc As Integer = 1 To 5
                    rowSum += ToLongOrZero(If(実計算Dgv.Rows(rr).Cells(cc).Value, "").ToString())
                Next
                実計算Dgv.Rows(rr).Cells(lastColIndex).Value = rowSum.ToString()
            Next

            ' 縦計
            For cc As Integer = 1 To 5
                Dim colSum As Long = 0
                For rr As Integer = 0 To dataRows - 1
                    colSum += ToLongOrZero(If(実計算Dgv.Rows(rr).Cells(cc).Value, "").ToString())
                Next
                実計算Dgv.Rows(totalsRowIndex).Cells(cc).Value = colSum.ToString()
            Next

            ' 最下行左のラベルと総合計
            実計算Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim grandTotal As Long = 0
            For c As Integer = 1 To 5
                grandTotal += ToLongOrZero(If(実計算Dgv.Rows(totalsRowIndex).Cells(c).Value, "").ToString())
            Next
            実計算Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandTotal.ToString()

            ' 書式設定: 右寄せ、ヘッダー中央、読み取り専用
            実計算Dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            実計算Dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            For Each col As DataGridViewColumn In 実計算Dgv.Columns
                col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
            Next
            実計算Dgv.ReadOnly = True
            実計算Dgv.RowHeadersVisible = False
        End Using
    End Sub


    ' T160 の順位最大値を取得するためのラッパー（実際はテーブルのレコード数を返す）
    Private Function GetMaxRankFromTable(tableName As String) As Integer
        Try
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Return 0

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' テーブル存在確認
                Dim schema = conn.GetSchema("Tables")
                Dim found As Boolean = False
                For Each row As DataRow In schema.Rows
                    Dim tname = row("TABLE_NAME").ToString()
                    Dim ttype = row("TABLE_TYPE").ToString()
                    If ttype = "TABLE" AndAlso String.Compare(tname, tableName, True) = 0 Then
                        found = True
                        Exit For
                    End If
                Next
                If Not found Then Return 0

                ' レコード数を取得
                Using cmd As New OleDbCommand($"SELECT COUNT(*) FROM [{tableName}]", conn)
                    Dim result = cmd.ExecuteScalar()
                    If result IsNot Nothing AndAlso Not IsDBNull(result) Then
                        Return CInt(result)
                    End If
                End Using

                Return 0
            End Using
        Catch ex As Exception
            Return 0
        End Try
    End Function

    Private Function ToLongOrZero(s As String) As Long
        Dim v As Long
        If Long.TryParse(s, v) Then
            Return v
        End If
        Return 0
    End Function

    ' DBヘルパー: 指定テーブルの数値カラムの最大値を取得する（使用されていない - 将来用に保持）
    Private Function GetMaxNumericValueFromTable(tableName As String) As Integer
        Try
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Return 0

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' テーブル存在確認
                Dim schema = conn.GetSchema("Tables")
                Dim found As Boolean = False
                For Each row As DataRow In schema.Rows
                    Dim tname = row("TABLE_NAME").ToString()
                    Dim ttype = row("TABLE_TYPE").ToString()
                    If ttype = "TABLE" AndAlso String.Compare(tname, tableName, True) = 0 Then
                        found = True
                        Exit For
                    End If
                Next
                If Not found Then Return 0

                Dim da As New OleDbDataAdapter($"SELECT * FROM [{tableName}]", conn)
                Dim dt As New DataTable()
                da.Fill(dt)
                If dt.Rows.Count = 0 Then Return 0

                Dim maxVal As Double = Double.MinValue
                For Each col As DataColumn In dt.Columns
                    Dim t = col.DataType
                    If t Is GetType(Integer) OrElse t Is GetType(Long) OrElse t Is GetType(Double) OrElse t Is GetType(Single) OrElse t Is GetType(Decimal) Then
                        For Each r As DataRow In dt.Rows
                            If Not IsDBNull(r(col)) Then
                                Dim v As Double = Convert.ToDouble(r(col))
                                If v > maxVal Then maxVal = v
                            End If
                        Next
                    End If
                Next
                If maxVal = Double.MinValue Then Return 0
                Return CInt(Math.Round(maxVal))
            End Using
        Catch ex As Exception
            Return 0
        End Try
    End Function

    ' データ構造定義
    Public Class ModelData
        <LoadColumn(0)> Public モデル連番 As Single
        <LoadColumn(1)> Public 出荷先数 As Single
        <LoadColumn(2)> Public アイテム数 As Single
        <LoadColumn(3)> Public バラ数量 As Single
        <LoadColumn(4)> Public 行数 As Single
        <LoadColumn(5)> Public E1 As Single
        <LoadColumn(6)> Public E2 As Single
        <LoadColumn(7)> Public E3 As Single
        <LoadColumn(8)> Public E4 As Single
        <LoadColumn(9)> Public E5 As Single
        <LoadColumn(10)> Public I1 As Single
        <LoadColumn(11)> Public I2 As Single
        <LoadColumn(12)> Public I3 As Single
        <LoadColumn(13)> Public I4 As Single
        <LoadColumn(14)> Public I5 As Single
        ' 行数01-25, バラ数01-25, ケース01-25, PL01-25, 容積01-25, 重量01-25も同様に定義
        ' (省略：必要に応じて追加)
    End Class

    ' 入力データ構造
    Public Class InputData
        Public 出荷先数 As Single
        Public アイテム数 As Single
    End Class

    ' 単一予測結果クラス
    Public Class SinglePrediction
        <ColumnName("Score")>
        Public Score As Single
    End Class
#End Region

#Region "画面操作"
    ' ========================================
    ' フォームロード
    ' ========================================
    Private Sub Tera計算EIQAI_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        フォルダー確認()

        MyDB = Dat保存Path & "\EIQ_AIモデル.accdb"
        Dim Saizu As Integer = 500 ' MB
        Access最適化(MyDB, Saizu)

        Try
            ' MLContextを初期化
            mlContext = New MLContext(seed:=0)
            modelFolder = Dat保存Path
            trainedModels = New Dictionary(Of String, ITransformer)
            predictionEngines = New Dictionary(Of String, PredictionEngine(Of InputData, SinglePrediction))

            ' モデルフォルダの確認
            If Not Directory.Exists(modelFolder) Then
                MsgBox("学習済モデルフォルダが見つかりません。" & vbCrLf &
                      $"フォルダ: {modelFolder}" & vbCrLf & vbCrLf &
                      "モデルファイル(.zip)を上記フォルダに配置してください。",
                      MsgBoxStyle.Exclamation, "フォルダ未検出")
            End If

            出力フィールド名リスト初期化()   ' 出力フィールド名リストを初期化
            学習済モデル読込()               ' 自動的にモデルを読み込む
            AI予測EIQマトリクス表Dgv初期化() ' AI予測EIQマトリクス表Dgvのヘッダー初期化

            ' 比較開始ボタンが押されるまで、表示チェック2Gbのラジオボタンを無効にする
            Set表示チェック2GbEnabled(False)

            Debug.WriteLine("modelFolder=" & modelFolder)
            Debug.WriteLine("MyDB=" & MyDB)

        Catch ex As Exception
            MsgBox($"初期化エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub
    ' ========================================
    ' 学習済モデル読込ボタン
    ' ========================================
    Private Async Sub 学習済モデル読込Bt_Click(sender As Object, e As EventArgs) Handles 学習済モデル読込Bt.Click
        学習済モデル読込Bt.Enabled = False
        モデル状態Lb.Text = "モデル: 読込中..."
        モデル状態Lb.ForeColor = Color.Orange
        Try
            Await Task.Run(Sub() 学習済モデル読込())
            If trainedModels.Count > 0 Then
                MsgBox($"{trainedModels.Count} 個のモデルを読み込みました。",
                      MsgBoxStyle.Information, "読込完了")
            End If
        Finally
            学習済モデル読込Bt.Enabled = True
        End Try
    End Sub
    ' ========================================
    ' 予測実行ボタン
    ' ========================================
    Private Async Sub 予測実行Bt_Click(sender As Object, e As EventArgs) Handles 予測実行Bt.Click
        予測実行Bt.Enabled = False
        Try
            出荷先数2Tb.Text = 出荷先数Tb.Text
            アイテム数2Tb.Text = アイテム数Tb.Text
            Await 予測実行()
        Finally
            予測実行Bt.Enabled = True
        End Try
    End Sub
    Private Sub EIQマトリクス表実行Bt_Click(sender As Object, e As EventArgs) Handles EIQマトリクス表実行Bt.Click
        AI予測EIQマトリクス表()
        AI予測従来計算E（)
        AI予測従来計算I（)
        matrixActive = True
    End Sub

    ' 表示単位のラジオボタンが変更されたら、マトリクスが有効な場合に再描画する
    Private Sub 抽出_表示単位_CheckedChanged(sender As Object, e As EventArgs) Handles 抽出_出庫回数Rb.CheckedChanged, 抽出_バラRb.CheckedChanged, 抽出_ケースRb.CheckedChanged, 抽出_PLRb.CheckedChanged, 抽出_容積Rb.CheckedChanged, 抽出_重量Rb.CheckedChanged
        If matrixActive Then
            AI予測EIQマトリクス表()
        End If
    End Sub
    Private Sub AI予測と実計算比較移動Bt_Click(sender As Object, e As EventArgs) Handles AI予測と実計算比較移動Bt.Click
        Tera計算EIQAI_Tc.SelectedTab = AI予測と実計算比較Tp
        比較画面クリア()
    End Sub
    Private Sub 比較開始Bt_Click(sender As Object, e As EventArgs) Handles 比較開始Bt.Click
        '予測計算2Gbを表示（予測計算1Gbと同じ内容)
        予測計算Gb作成()
        Set表示チェック2GbEnabled(True)

        EMax = GetMaxRankFromTable("T160_全出荷E集計")
        IMax = GetMaxRankFromTable("T160_全出荷I集計")

        Dim 計算E数 = CInt(出荷先数2Tb.Text)
        Dim E採用判定値 As Double = CDbl(計算E数 / EMax)
        '  IMaxはT760_全出荷I集計 'T160からT760作成()で取得
        Dim 計算I数 = CInt(アイテム数Tb.Text)
        Dim I採用判定値 As Double = CDbl(計算I数 / IMax)

        MyTable = "T800_計算対象"

        T350_計算対象1(E採用判定値)
        T350_計算対象2(I採用判定値)
        T360_全出荷E集計()
        T360_全出荷I集計()
        T400_クロス集計()

        T600_手計算()
        'T600_手計算作成()
        Update実計算表示FromT600()

        誤差表示()
    End Sub

    ' 指定した enabled 値で比較タブ側の表示単位ラジオボタン群を有効/無効化する
    Private Sub Set表示チェック2GbEnabled(enabled As Boolean)
        Try
            抽出_出庫回数2Rb.Enabled = enabled
            抽出_バラ2Rb.Enabled = enabled
            抽出_ケース2Rb.Enabled = enabled
            抽出_PL2Rb.Enabled = enabled
            抽出_容積2Rb.Enabled = enabled
            抽出_重量2Rb.Enabled = enabled
        Catch
            ' Designerでコントロールが未定義の場合は無視
        End Try
    End Sub
    Private Sub EI変更Tb_Click(sender As Object, e As EventArgs) Handles EI変更Tb.Click
        ' 1番目（インデックス0）のタブに移動
        Tera計算EIQAI_Tc.SelectedTab = Tera計算EIQAI_Tc.TabPages("AI予測計算Tp")
    End Sub
    Private Sub 比較画面クリア()
        Try
            ' --- 1. 描画を一時停止してチラつきを抑える ---
            Me.SuspendLayout()

            ' --- 2. テキストボックスのクリア ---
            Dim tbNames As String() = {
                "AI_IAI_Tb", "AI_IA2_Tb", "AI_IB_Tb", "AI_IC_Tb", "AI_ID_Tb", "AI_IKK_Tb",
                "AI_EAI_Tb", "AI_EA2_Tb", "AI_EB_Tb", "AI_EC_Tb", "AI_ED_Tb", "AI_EKK_Tb",
                "実_EAI_Tb", "実_EA2_Tb", "実_EB_Tb", "実_EC_Tb", "実_ED_Tb", "実_EKK_Tb",
                "実_IAI_Tb", "実_IA2_Tb", "実_IB_Tb", "実_IC_Tb", "実_ID_Tb", "実_IKK_Tb",
                "比_EAI_Tb", "比_EA2_Tb", "比_EB_Tb", "比_EC_Tb", "比_ED_Tb", "比_EKK_Tb",
                "比_IAI_Tb", "比_IA2_Tb", "比_IB_Tb", "比_IC_Tb", "比_ID_Tb", "比_IKK_Tb"
            }

            For Each name As String In tbNames
                Dim ctrl As Control() = Me.Controls.Find(name, True)
                If ctrl.Length > 0 AndAlso TypeOf ctrl(0) Is TextBox Then
                    DirectCast(ctrl(0), TextBox).Clear() ' Text="" より Clear() の方が明示的
                End If

            Next

            ' --- 3. DataGridViewのクリア ---
            AI予測従来計算アイテムDgv.Columns.Clear()
            実計算Dgv.Columns.Clear()
            誤差Dgv.Columns.Clear()

            ' --- 4. フラグのリセット ---
            matrixActive = False

            ' --- 5. レイアウトを再開して強制的に反映 ---
            Me.ResumeLayout()
            Me.Refresh() ' これでフォーム全体の描画を強制実行

        Catch
            ' クリア中の例外は無視
        End Try
    End Sub

    ' T160 から T760 を作成し、EMax/IMax（T760 のレコード数）を設定する
    Public Sub T160からT760作成()
        Try
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then
                Throw New Exception("DBファイルが見つかりません")
            End If

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' 既存の T760 テーブルを削除（存在する場合）
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE [T760_全出荷E集計]", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE [T760_全出荷I集計]", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                ' T160 テーブルの存在確認
                Dim schema = conn.GetSchema("Tables")
                Dim hasT160E As Boolean = False
                Dim hasT160I As Boolean = False
                For Each row As DataRow In schema.Rows
                    Dim tname = row("TABLE_NAME").ToString()
                    Dim ttype = row("TABLE_TYPE").ToString()
                    If ttype = "TABLE" Then
                        If String.Compare(tname, "T160_全出荷E集計", True) = 0 Then hasT160E = True
                        If String.Compare(tname, "T160_全出荷I集計", True) = 0 Then hasT160I = True
                    End If
                Next

                ' 存在する場合はコピー（SELECT INTO）で T760 を作成
                If hasT160E Then
                    Using cmd As New OleDbCommand("SELECT * INTO T760_全出荷E集計 FROM T160_全出荷E集計", conn)
                        Try
                            cmd.ExecuteNonQuery()
                        Catch
                        End Try
                    End Using
                End If

                If hasT160I Then
                    Using cmd As New OleDbCommand("SELECT * INTO T760_全出荷I集計 FROM T160_全出荷I集計", conn)
                        Try
                            cmd.ExecuteNonQuery()
                        Catch
                        End Try
                    End Using
                End If

                ' レコード数を取得して EMax/IMax に格納
                Try
                    If hasT160E Then
                        Using cmd As New OleDbCommand("SELECT COUNT(*) FROM T760_全出荷E集計", conn)
                            Dim cnt = cmd.ExecuteScalar()
                            If cnt IsNot Nothing AndAlso Not IsDBNull(cnt) Then
                                EMax = CInt(cnt)
                            Else
                                EMax = 0
                            End If
                        End Using
                    Else
                        EMax = 0
                    End If
                Catch
                    EMax = 0
                End Try

                Try
                    If hasT160I Then
                        Using cmd As New OleDbCommand("SELECT COUNT(*) FROM T760_全出荷I集計", conn)
                            Dim cnt = cmd.ExecuteScalar()
                            If cnt IsNot Nothing AndAlso Not IsDBNull(cnt) Then
                                IMax = CInt(cnt)
                            Else
                                IMax = 0
                            End If
                        End Using
                    Else
                        IMax = 0
                    End If
                Catch
                    IMax = 0
                End Try

            End Using

        Catch ex As Exception
            ' 失敗した場合は最大値を0に設定
            EMax = 0
            IMax = 0
        End Try
    End Sub

    Private Sub 誤差表示()
        Try
            ' 必要なDataGridViewが存在するかチェック
            If 実計算Dgv Is Nothing OrElse AI予測EIQマトリクス表_複写Dgv Is Nothing OrElse 誤差Dgv Is Nothing Then
                Return
            End If

            ' マトリクスは 5x5 (データ行) + 合計行 で扱う
            Const dataRows As Integer = 5
            Const dataCols As Integer = 5

            ' 解析用のパーサ
            Dim parse As Func(Of String, Double) = Function(s As String) As Double
                                                       Dim d As Double
                                                       If String.IsNullOrWhiteSpace(s) Then Return 0.0
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, d) Then Return d
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, d) Then Return d
                                                       Return 0.0
                                                   End Function

            ' 誤差Dgv の初期化（列ヘッダーは実・予測と同じ形式）
            誤差Dgv.Columns.Clear()
            誤差Dgv.Columns.Add("GPLEランク", "GPLEランク")
            誤差Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            誤差Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            誤差Dgv.Columns.Add("GPLI_B", "GPLI_B")
            誤差Dgv.Columns.Add("GPLI_C", "GPLI_C")
            誤差Dgv.Columns.Add("GPLI_D", "GPLI_D")
            誤差Dgv.Columns.Add("横計", "横計")

            ' 列幅設定
            For i As Integer = 0 To 誤差Dgv.Columns.Count - 1
                If i = 0 Then
                    誤差Dgv.Columns(i).Width = 90
                Else
                    誤差Dgv.Columns(i).Width = 83
                End If
            Next

            ' 行ラベルを作成 (データ行 + 合計行)
            誤差Dgv.Rows.Clear()
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                誤差Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next
            ' 合計行を確保
            While 誤差Dgv.Rows.Count < dataRows + 1
                誤差Dgv.Rows.Add()
            End While
            Dim lastColIndex As Integer = 誤差Dgv.Columns.Count - 1
            Dim totalsRowIndex As Integer = dataRows

            ' セルごとに誤差を計算: (予測 - 実) / 実 * 100
            For r As Integer = 0 To dataRows - 1
                For c As Integer = 1 To dataCols
                    Dim actualStr As String = If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim predStr As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim actual As Double = parse(actualStr)
                    Dim pred As Double = parse(predStr)

                    Dim pctText As String
                    If actual = 0 Then
                        If pred = 0 Then
                            pctText = "0.00%"
                        Else
                            pctText = "N/A"
                        End If
                    Else
                        Dim pct As Double = (pred - actual) / Math.Abs(actual) * 100.0
                        pctText = pct.ToString("F2") & "%"

                    End If

                    誤差Dgv.Rows(r).Cells(c).Value = pctText
                Next

                ' 行の横計(合計値の誤差): 実と予測の行合計で計算
                Dim actualRowSum As Double = 0
                Dim predRowSum As Double = 0
                For c As Integer = 1 To dataCols
                    actualRowSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predRowSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim rowPct As String
                If actualRowSum = 0 Then
                    If predRowSum = 0 Then
                        rowPct = "0.00%"
                    Else
                        rowPct = "N/A"
                    End If
                Else
                    rowPct = ((predRowSum - actualRowSum) / Math.Abs(actualRowSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(r).Cells(lastColIndex).Value = rowPct
            Next

            ' 縦計（各列）の誤差を下段に表示
            For c As Integer = 1 To dataCols
                Dim actualColSum As Double = 0
                Dim predColSum As Double = 0
                For r As Integer = 0 To dataRows - 1
                    actualColSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predColSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim colPct As String
                If actualColSum = 0 Then
                    If predColSum = 0 Then
                        colPct = "0.00%"
                    Else
                        colPct = "N/A"
                    End If
                Else
                    colPct = ((predColSum - actualColSum) / Math.Abs(actualColSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(totalsRowIndex).Cells(c).Value = colPct
            Next

            ' 最下行左のラベルと総合合計の誤差
            誤差Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim actualGrand As Double = 0
            Dim predGrand As Double = 0
            For c As Integer = 1 To 5
                Dim aVal As String = If(実計算Dgv.Rows.Count > totalsRowIndex AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                Dim pVal As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > totalsRowIndex AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                actualGrand += parse(aVal)
                predGrand += parse(pVal)
            Next
            Dim grandPct As String
            If actualGrand = 0 Then
                If predGrand = 0 Then
                    grandPct = "0.00%"
                Else
                    grandPct = "N/A"
                End If
            Else
                grandPct = ((predGrand - actualGrand) / Math.Abs(actualGrand) * 100.0).ToString("F2") & "%"

            End If
            誤差Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandPct

            ' --- 出荷先数 / アイテム数 の％計算を誤差計算Gbのテキストボックスに表示 ---
            Try
                ' E1..E5
                Dim actualEs() As String = {実_EAI_Tb.Text, 実_EA2_Tb.Text, 実_EB_Tb.Text, 実_EC_Tb.Text, 実_ED_Tb.Text}
                Dim predEs() As String = {AI_EAI_Tb.Text, AI_EA2_Tb.Text, AI_EB_Tb.Text, AI_EC_Tb.Text, AI_ED_Tb.Text}
                Dim 比Es() As TextBox = {比_EAI_Tb, 比_EA2_Tb, 比_EB_Tb, 比_EC_Tb, 比_ED_Tb}
                For i As Integer = 0 To 4
                    Dim a As Double = parse(If(actualEs(i), ""))
                    Dim p As Double = parse(If(predEs(i), ""))
                    Dim t As String
                    If a = 0 Then
                        If p = 0 Then
                            t = "0.00%"
                        Else
                            t = "N/A"
                        End If
                    Else
                        t = ((p - a) / Math.Abs(a) * 100.0).ToString("F2") & "%"
                    End If
                    比Es(i).Text = t
                Next

                ' I1..I5
                Dim actualIs() As String = {実_IAI_Tb.Text, 実_IA2_Tb.Text, 実_IB_Tb.Text, 実_IC_Tb.Text, 実_ID_Tb.Text}
                Dim predIs() As String = {AI_IAI_Tb.Text, AI_IA2_Tb.Text, AI_IB_Tb.Text, AI_IC_Tb.Text, AI_ID_Tb.Text}
                Dim 比Is() As TextBox = {比_IAI_Tb, 比_IA2_Tb, 比_IB_Tb, 比_IC_Tb, 比_ID_Tb}
                For i As Integer = 0 To 4
                    Dim a As Double = parse(If(actualIs(i), ""))
                    Dim p As Double = parse(If(predIs(i), ""))
                    Dim t As String
                    If a = 0 Then
                        If p = 0 Then
                            t = "0.00%"
                        Else
                            t = "N/A"
                        End If
                    Else
                        t = ((p - a) / Math.Abs(a) * 100.0).ToString("F2") & "%"
                    End If
                    比Is(i).Text = t
                Next

                ' 合計（出荷先数・アイテム数）
                Dim actualETotal As Double = parse(実_EKK_Tb.Text)
                Dim predETotal As Double = parse(AI_EKK_Tb.Text)
                If actualETotal = 0 Then
                    If predETotal = 0 Then
                        比_EKK_Tb.Text = "0.00%"
                    Else
                        比_EKK_Tb.Text = "N/A"
                    End If
                Else
                    比_EKK_Tb.Text = ((predETotal - actualETotal) / Math.Abs(actualETotal) * 100.0).ToString("F2") & "%"
                End If

                Dim actualITotal As Double = parse(実_IKK_Tb.Text)
                Dim predITotal As Double = parse(AI_IKK_Tb.Text)
                If actualITotal = 0 Then
                    If predITotal = 0 Then
                        比_IKK_Tb.Text = "0.00%"
                    Else
                        比_IKK_Tb.Text = "N/A"
                    End If
                Else
                    比_IKK_Tb.Text = ((predITotal - actualITotal) / Math.Abs(actualITotal) * 100.0).ToString("F2") & "%"
                End If
            Catch
                ' 表示更新時の例外は無視
            End Try

            ' 書式設定
            誤差Dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            誤差Dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            For Each col As DataGridViewColumn In 誤差Dgv.Columns
                col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
            Next
            誤差Dgv.ReadOnly = True
            誤差Dgv.RowHeadersVisible = False

        Catch ex As Exception
            ' UI更新中のエラーは無視
        End Try
    End Sub

    Private Sub 誤差表示1()
        Try
            ' 必要なDataGridViewが存在するかチェック
            If 実計算Dgv Is Nothing OrElse AI予測EIQマトリクス表_複写Dgv Is Nothing OrElse 誤差Dgv Is Nothing Then
                Return
            End If

            ' マトリクスは 5x5 (データ行) + 合計行 で扱う
            Const dataRows As Integer = 5
            Const dataCols As Integer = 5

            ' 解析用のパーサ
            Dim parse As Func(Of String, Double) = Function(s As String) As Double
                                                       Dim d As Double
                                                       If String.IsNullOrWhiteSpace(s) Then Return 0.0
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, d) Then Return d
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, d) Then Return d
                                                       Return 0.0
                                                   End Function

            ' 誤差Dgv の初期化（列ヘッダーは実・予測と同じ形式）
            誤差Dgv.Columns.Clear()
            誤差Dgv.Columns.Add("GPLEランク", "GPLEランク")
            誤差Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            誤差Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            誤差Dgv.Columns.Add("GPLI_B", "GPLI_B")
            誤差Dgv.Columns.Add("GPLI_C", "GPLI_C")
            誤差Dgv.Columns.Add("GPLI_D", "GPLI_D")
            誤差Dgv.Columns.Add("横計", "横計")

            ' 列幅設定
            For i As Integer = 0 To 誤差Dgv.Columns.Count - 1
                If i = 0 Then
                    誤差Dgv.Columns(i).Width = 90
                Else
                    誤差Dgv.Columns(i).Width = 83
                End If
            Next

            ' 行ラベルを作成 (データ行 + 合計行)
            誤差Dgv.Rows.Clear()
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                誤差Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next
            ' 合計行を確保
            While 誤差Dgv.Rows.Count < dataRows + 1
                誤差Dgv.Rows.Add()
            End While

            Dim lastColIndex As Integer = 誤差Dgv.Columns.Count - 1
            Dim totalsRowIndex As Integer = dataRows

            ' セルごとに誤差を計算: (予測 - 実) / 実 * 100
            For r As Integer = 0 To dataRows - 1
                For c As Integer = 1 To dataCols
                    Dim actualStr As String = If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim predStr As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim actual As Double = parse(actualStr)
                    Dim pred As Double = parse(predStr)

                    Dim pctText As String
                    If actual = 0 Then
                        If pred = 0 Then
                            pctText = "0.00%"
                        Else
                            pctText = "N/A"
                        End If
                    Else
                        Dim pct As Double = (pred - actual) / Math.Abs(actual) * 100.0
                        pctText = pct.ToString("F2") & "%"

                    End If

                    誤差Dgv.Rows(r).Cells(c).Value = pctText
                Next

                ' 行の横計(合計値の誤差): 実と予測の行合計で計算
                Dim actualRowSum As Double = 0
                Dim predRowSum As Double = 0
                For c As Integer = 1 To dataCols
                    actualRowSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predRowSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim rowPct As String
                If actualRowSum = 0 Then
                    If predRowSum = 0 Then
                        rowPct = "0.00%"
                    Else
                        rowPct = "N/A"
                    End If
                Else
                    rowPct = ((predRowSum - actualRowSum) / Math.Abs(actualRowSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(r).Cells(lastColIndex).Value = rowPct
            Next

            ' 縦計（各列）の誤差を下段に表示
            For c As Integer = 1 To dataCols
                Dim actualColSum As Double = 0
                Dim predColSum As Double = 0
                For r As Integer = 0 To dataRows - 1
                    actualColSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predColSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim colPct As String
                If actualColSum = 0 Then
                    If predColSum = 0 Then
                        colPct = "0.00%"
                    Else
                        colPct = "N/A"
                    End If
                Else
                    colPct = ((predColSum - actualColSum) / Math.Abs(actualColSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(totalsRowIndex).Cells(c).Value = colPct
            Next

            ' 最下行左のラベルと総合合計の誤差
            誤差Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim actualGrand As Double = 0
            Dim predGrand As Double = 0
            For c As Integer = 1 To 5
                Dim aVal As String = If(実計算Dgv.Rows.Count > totalsRowIndex AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                Dim pVal As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > totalsRowIndex AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                actualGrand += parse(aVal)
                predGrand += parse(pVal)
            Next
            Dim grandPct As String
            If actualGrand = 0 Then
                If predGrand = 0 Then
                    grandPct = "0.00%"
                Else
                    grandPct = "N/A"
                End If
            Else
                grandPct = ((predGrand - actualGrand) / Math.Abs(actualGrand) * 100.0).ToString("F2") & "%"

            End If
            誤差Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandPct

        Catch ex As Exception
            ' UI更新中のエラーは無視
        End Try
    End Sub

    ' T600 の集計結果をダイアログ表示用の変数に設定
    Private Sub SetT600集計結果ToDialogValues()
        Try
            ' T600_手計算 が存在するか確認
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then
                Throw New Exception("DBファイルが見つかりません")
            End If

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' T600_手計算 の単一行を取得
                Dim sql As String = "SELECT * FROM T600_手計算"
                Dim dt As New DataTable()
                Using da As New OleDbDataAdapter(sql, conn)
                    da.Fill(dt)
                End Using
                If dt.Rows.Count = 0 Then Return

                Dim r As DataRow = dt.Rows(0)

                ' 基本フィールドをテキストボックスに設定
                Try
                    出荷先数Tb.Text = If(dt.Columns.Contains("出荷先数") AndAlso Not IsDBNull(r("出荷先数")), Convert.ToDouble(r("出荷先数")).ToString("F0"), "")
                Catch
                End Try
                Try
                    アイテム数Tb.Text = If(dt.Columns.Contains("アイテム数") AndAlso Not IsDBNull(r("アイテム数")), Convert.ToDouble(r("アイテム数")).ToString("F0"), "")
                Catch
                End Try

                ' pred 辞書を作成（予測側のロジックと同様のキー）
                Dim pred As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
                ' E1..E5, I1..I5
                For i As Integer = 1 To 5
                    Dim ec = $"E{i}"
                    If dt.Columns.Contains(ec) AndAlso Not IsDBNull(r(ec)) Then pred(ec) = Convert.ToDouble(r(ec)).ToString("F0") Else pred(ec) = "0"
                    Dim ic = $"I{i}"
                    If dt.Columns.Contains(ic) AndAlso Not IsDBNull(r(ic)) Then pred(ic) = Convert.ToDouble(r(ic)).ToString("F0") Else pred(ic) = "0"
                Next
                ' 行数01-25, バラ数01-25, ケース01-25, PL01-25, 容積01-25, 重量01-25
                Dim prefixes As String() = {"行数", "バラ数", "ケース", "PL", "容積", "重量"}
                For Each pfx As String In prefixes
                    For i As Integer = 1 To 25
                        Dim colName = pfx & i.ToString("D2")
                        If dt.Columns.Contains(colName) AndAlso Not IsDBNull(r(colName)) Then
                            pred(colName) = Convert.ToDouble(r(colName)).ToString("F0")
                        Else
                            pred(colName) = "0"
                        End If
                    Next
                Next

                ' --- 詳細ダイアログ用の設定 ---
                ' EIQマトリクス表・従来計算 各タブの初期化処理を呼び出し
                AI予測従来計算E（)
                AI予測従来計算I（)

                ' 出荷先数、アイテム数、合計欄のみ元の値を保持
                Dim 検証用出荷先数 As Double = 0
                Dim 検証用アイテム数 As Double = 0
                Double.TryParse(出荷先数Tb.Text, 検証用出荷先数)
                Double.TryParse(アイテム数Tb.Text, 検証用アイテム数)

                ' E1..E5
                For i As Integer = 1 To 5
                    Dim key As String = "E" & i.ToString()
                    Dim predVal As Double = ToDoubl
